import * as geometric from 'geometric';
import * as util from '@/utils';
import store from '@/store';

interface DraggableContainerConfig {
    minScale: number;
    maxScale: number;
    transformOrginX: number;
    transformOrginY: number;
}

export function getScrollEvent(){
    return navigator.userAgent.toLowerCase().indexOf('firefox') != -1?'DOMMouseScroll':'wheel';
}

const  shortcutKeys = {
    32: 'space',
    187: '+',
    189: '-'
}

export class DraggableContainer {
    public container;
    public containerParent;
    public param;
    public defaultConfig;
    public isMouseDown: boolean;
    public isMouseOver: boolean;
    public mouseDownX: number;
    public mouseDownY: number;
    public moveX: number;
    public moveY: number;
    public scale: number;
    public preX: number;
    public preY: number;
    public disabled: boolean;
    public isKeydown: boolean;
    public keydownCode: string;

    constructor(container,param){
        this.container = container;
        this.containerParent = this.container.parentNode;
        this.container.style.position = "absolute";
        this.param = param;
        this.defaultConfig = {
            minScale: param.minScale||0.2,
            maxScale: param.maxScale||3,
            transformOrginX: 0, //以鼠标为中心缩放
            transformOrginY: 0
        }
        this.isMouseDown = false;
        this.isMouseOver = false;
        this.mouseDownX = 0;
        this.mouseDownY = 0;
        this.moveX = 0;
        this.moveY = 0;
        this.scale = 1;
        this.preX = 0;
        this.preY = 0;
        this.disabled = false;

        this.isKeydown = false;
        this.keydownCode = '';

        this.initData();
        this.transformInit();
        this.bindEvent();
    }
    public initData(){
        this.container.style.transformOrigin = `${this.defaultConfig.transformOrginX}px ${this.defaultConfig.transformOrginY}px`;
        this.isMouseDown = false;
        this.isMouseOver = false;
        this.mouseDownX = 0;
        this.mouseDownY = 0;
    }
    public transformInit(param:any={}){
        this.moveX = param.moveX||0;
        this.moveY = param.moveY||0;
        this.scale = param.scale||1;
        this.preX = 0;
        this.preY = 0;
        this.transformCanvas();
    }
    public zoomEvent(type){
        const centerX = this.container.offsetWidth/2;
        const centerY = this.container.offsetHeight/2;
        if(type=='zoom-in'){ //放大
            this.scaleEvent(1.2,centerX,centerY);
        }else{
           this.scaleEvent(0.8,centerX,centerY);
        }
        if(this.param.wheelEvent)this.param.wheelEvent(this.scale);
    }
    public bindEvent(){
        this.containerParent.addEventListener('mousedown',this.mousedownEvent.bind(this));
        this.containerParent.addEventListener('mouseover', this.mouseoverEvent.bind(this));
        this.containerParent.addEventListener('mouseout', this.mouseoutEvent.bind(this));
        this.containerParent.addEventListener(getScrollEvent(),this.wheelEvent.bind(this));
        document.addEventListener('mousemove', this.documentMousemoveEvent.bind(this));
        document.addEventListener('mouseup', this.documentMouseupEvent.bind(this));
        //增加快捷键
        document.addEventListener('keydown', this.documentKeydownEvent.bind(this));
        document.addEventListener('keyup', this.documentKeyupEvent.bind(this));
    }
    private documentKeydownEvent(e){
        const keyValue = shortcutKeys[e.keyCode];
        const baseKey = util.isMac()?e.metaKey:e.ctrlKey;
        if(baseKey&&keyValue == '+'){
            e.preventDefault();
            //放大
            this.zoomEvent('zoom-in');

        }else if(baseKey&&keyValue == '-'){
            e.preventDefault();
            //缩小
            this.zoomEvent('zoom-up');
        }
    }

    private documentKeyupEvent(e){

    }

    public windowToWrap( x , y){
        var bbox = this.containerParent.getBoundingClientRect();
        return {x:x-bbox.left, y:y-bbox.top}
    }
    public mousedownEvent(e){
        if(this.disabled)return
        if(e.target == this.container || e.target == this.containerParent){
            this.isMouseDown = true;
            this.mouseDownX = e.clientX;
            this.mouseDownY = e.clientY;
            this.preX = this.moveX;
            this.preY = this.moveY;
        }
    }
    public documentMousemoveEvent(e){
        if(!this.isMouseDown){
            return
        }
        const diffx = this.mouseDownX - e.clientX;
        const diffy = this.mouseDownY - e.clientY;
        this.moveX = this.preX - diffx;
        this.moveY = this.preY - diffy;
        this.transformCanvas();
    }
    public documentMouseupEvent(e){
        this.isMouseDown = false;
    }
    public mouseoverEvent(e){
        this.isMouseOver = true;
    }
    public mouseoutEvent(e){
        this.isMouseOver = true;
    }
    public wheelEvent(e) {
        if(this.disabled)return
        e.preventDefault();
        if(!this.isMouseOver){
            return
        }
        
        const detail = (e.wheelDelta || e.detail)||0;
        const scrollNum = detail==0?1:detail;
        const DEG = 0.1;//系数
        const zoom = scrollNum / Math.abs(scrollNum); //放大或缩小
        const actionScale = 1 + zoom * DEG;
        /*if (this.scale * actionScale < this.defaultConfig.minScale ||this.scale * actionScale > this.defaultConfig.maxScale) {
            return
        }*/
        const pointTowrap = this.windowToWrap(e.clientX,e.clientY);
        /*const x1 = this.moveX;
        const y1 = this.moveY;
        this.moveX = (x1 - pointTowrap.x)*actionScale+pointTowrap.x;
        this.moveY = (y1 - pointTowrap.y)*actionScale+pointTowrap.y;
        this.scale *= actionScale;
        this.transformCanvas();*/

        this.scaleEvent(actionScale,pointTowrap.x,pointTowrap.y);
        if(this.param.wheelEvent)this.param.wheelEvent(this.scale,e);
    }
    public scaleEvent(actionScale,centerX,centerY){
        if (this.scale * actionScale < this.defaultConfig.minScale ||this.scale * actionScale > this.defaultConfig.maxScale) {
            return
        }
        const x1 = this.moveX;
        const y1 = this.moveY;
        this.moveX = (x1 - centerX) * actionScale + centerX;
        this.moveY = (y1 - centerY) * actionScale + centerY;
        this.scale *= actionScale;
        this.transformCanvas();
    }
    public transformCanvas() {
        this.container.style.transform = `matrix(${this.scale},0,0,${this.scale},${this.moveX},${this.moveY})`;
        if(this.param.transformEvent)this.param.transformEvent(this.scale,this.moveX,this.moveY);
    }
    public getTransform(){
        return {
            scale: this.scale,
            moveX: this.moveX,
            moveY: this.moveY,
            transformOrginX: 0,
            transformOrginY: 0
        }
    }
    public getViewCoordinate(x,y){
        return { x: (x+this.moveX)*this.scale, y: (y+this.moveY)*this.scale }
    }
    public destroy() {
        this.containerParent.removeEventListener("mousedown", this.mousedownEvent);
        document.removeEventListener("mousemove", this.documentMousemoveEvent);
        document.removeEventListener("mouseup", this.documentMouseupEvent);
        document.removeEventListener("keydown", this.documentKeydownEvent);
        document.removeEventListener("keyup", this.documentKeyupEvent);
        this.containerParent.removeEventListener("mouseover", this.mouseoverEvent);
        this.containerParent.removeEventListener("mouseout", this.mouseoutEvent);
        this.containerParent.removeEventListener(getScrollEvent(), this.wheelEvent);
    }
}


//设置节点属性
export const setElementAttribute = function(ele, attr) {
    if(!ele||ele.length==0){
        return
    }
    if(ele.length&&ele.length>0){
        for(let i=0;i<ele.length;i++){
            for (let key in attr) {
                ele[i].setAttribute(key, attr[key])
            }
        }
        return
    };
    for (let key in attr) {
        ele.setAttribute(key, attr[key])
    }
}

export const removeElementAttribute = function(ele, attr) {
    if(!ele){
        return
    }
    if(ele.length&&ele.length>0){
        for(let i=0;i<ele.length;i++){
            attr.map(item=>{
                if(ele.hasAttribute(item))ele.removeAttribute(item)
            })
        }
        return
    }
    attr.map(item=>{
        if(ele.hasAttribute(item))ele.removeAttribute(item)
    })
}

export const svgElmentIsInRectSelected = function(node,rect){
    let x = parseInt(rect.getAttribute("x")),
            y = parseInt(rect.getAttribute("y")),
            w = parseInt(rect.getAttribute("width")),
            h = parseInt(rect.getAttribute("height"));
    let isIn = false;
    if(node.nodeName == "polygon"){
        let points = node.animatedPoints;
        for(let i=0;i<points.length;i++){
            if(pointIsInRect(points[i])){
                isIn = true;
                break;
            }
        }
    }else if(node.nodeName == "text"||node.nodeName == "image"){
        isIn = pointIsInRect({x: parseInt(node.getAttribute("x")),y: parseInt(node.getAttribute("y"))})
    }

    return isIn


    function pointIsInRect(p){
        if(p.x>x && p.x<(x+w) && p.y>y && p.y<(y+h)){
            return true
        }

        return false;

    }
}

export function createSvgElement(type,properties?: any){
    let element = document.createElementNS('http://www.w3.org/2000/svg', type);
    setElementAttribute(element, properties||{});
    return element
}

export function getBorderPointCollect(svgElement){
    //对path进行属性解析
    try{
        const d = svgElement.getAttribute('d');
        if(!d)return []
        const collect = d.match(/[M|L|C][\s|\-|\d|\.|,]+\d/g);
        let result: any = [];
        collect.forEach(item=>{
            const order = item.match(/[a-zA-Z]/g);
            if(!util.isEmpty(order)){
                const o = order[0];
                const numberStr = item.replace(/[a-zA-Z]/g,'').trim();
                if(['M','L'].includes(o)){
                    const point = numberStr.split(' ').map(n=>parseFloat(n));
                    result.push([o,point]);
                }else if(o == 'C'){
                    const points = numberStr.split(',').map(v=>{
                        return v.trim().split(' ').map(n=>parseFloat(n));
                    })
                    result.push([o,...points]);
                }
            }
        })
        return result
    }catch(err){
        console.log(err);
        return null
    }
    
}

export function createDrawingSvgElement(type, point?:[number,number]) {
    const mapScale = store.state['mapDesign'].mapScale;
    let element = createSvgElement(type);
    let properties = {};
    if (type == 'circle') {
        properties = {
            "r": 3/mapScale,
            "fill": "#FFFFFF",
            "stroke": "#f5cb3e",
            "stroke-width": 1/mapScale
        }
        if(point){
            properties["cx"] = point[0];
            properties["cy"] = point[1];
        }
    } else if (type == 'path') {
        properties = {
            "d": "",
            "stroke": "#f5cb3e",
            "stroke-width": 1/mapScale,
            "fill": "none"
        }
        if(point){
            properties["d"] = `M ${point[0]} ${point[1]} `;
        }
    }

    setElementAttribute(element, properties);
    return element
}

export function createControlCircle(point: [number,number], type:number|undefined = 1){
    const mapScale = store.state['mapDesign'].mapScale;
    let element = createSvgElement('circle');
    let properties = {
        "cx": point[0],
        "cy": point[1],
        "r": 4/mapScale,
        "fill": "#FFFFFF",
        "stroke": "#303133",
        "stroke-width": 1/mapScale
    }
    if(type == 2){
        properties["fill"] = "#303133";
        properties["r"] = 3/mapScale;
    }

    if(type == 3){
        properties["r"] = 6/mapScale;
        properties["stroke-width"] = 2/mapScale;
    }

    if(type == 4){
        properties["stroke"] = '#67C23A'; 
        properties["r"] = 4;
        properties["stroke-width"] = 1;
    }

    setElementAttribute(element, properties);
    return element
}

export function createCurveControlLine(p1,p2){
    const mapScale = store.state['mapDesign'].mapScale;
    let element = createSvgElement('line');
    let properties = {
        "x1": p1[0],
        "y1": p1[1],
        "x2": p2[0],
        "y2": p2[1],
        "stroke": "#303133",
        "stroke-width": 1/mapScale
    }
    setElementAttribute(element, properties);
    return element
}

export function getSvgLineData(el){
    const x1 = parseFloat(el.getAttribute('x1'));
    const y1 = parseFloat(el.getAttribute('y1'));
    const x2 = parseFloat(el.getAttribute('x2'));
    const y2 = parseFloat(el.getAttribute('y2'));
    return [ [x1,y1], [x2,y2] ] 
}

export function curveJoin(p){
    return `C ${p[0]} ${p[1]}, ${p[2]} ${p[3]}, ${p[4]} ${p[5]}`
}

export const commonStyles = {
    "stroke": 'rgba(245,203,62,1)',
    "stroke-width": 1,
    "fill": 'rgba(245,203,62,0.3)'
}

//使用贝塞尔曲线绘制圆或椭圆--(x,y为圆心)
export function createCirclePath(x: number, y: number, w: number, h: number, path: SVGPathElement){
    var kappa = 0.5522848, // 作为计算两个控制点的偏移量的常数，由圆曲率公式推导
    ox = (w / 2) * kappa, // 控制点水平方向偏移量
    oy = (h / 2) * kappa, // 控制点垂直方向偏移量
    xe = x + w,           // x方向结束位置
    ye = y + h,           // y方向结束位置
    xm = x + w / 2,       // x方向中点
    ym = y + h / 2;       // y方向中点 
    var c1 = [x, ym - oy, xm - ox, y, xm, y];
    var c2 = [xm + ox, y, xe, ym - oy, xe, ym];
    var c3 = [xe, ym + oy, xm + ox, ye, xm, ye];
    var c4 = [xm - ox, ye, x, ym + oy, x, ym];
    setElementAttribute(path,{
        ...commonStyles,
        d: `M ${x} ${ym} ${curveJoin(c1)} ${curveJoin(c2)} ${curveJoin(c3)} ${curveJoin(c4)} Z`
    })
}

export function createHalfCirclePath(x: number, y: number, w: number, h: number, path: SVGPathElement){
    const { start, curves } = getCurveCirclePath(x,y,w,h);
    setElementAttribute(path,{
        ...commonStyles,
        d: `M ${start[0]} ${start[1]} ${curveJoin(curves[0])} ${curveJoin(curves[1])} Z`
    })
}

export function getCurveCirclePath(x: number, y: number, w: number, h: number){
    var kappa = 0.5522848, // 作为计算两个控制点的偏移量的常数，由圆曲率公式推导
    ox = (w / 2) * kappa, // 控制点水平方向偏移量
    oy = (h / 2) * kappa, // 控制点垂直方向偏移量
    xe = x + w,           // x方向结束位置
    ye = y + h,           // y方向结束位置
    xm = x + w / 2,       // x方向中点
    ym = y + h / 2;       // y方向中点 
    var c1 = [x, ym - oy, xm - ox, y, xm, y];
    var c2 = [xm + ox, y, xe, ym - oy, xe, ym];
    var c3 = [xe, ym + oy, xm + ox, ye, xm, ye];
    var c4 = [xm - ox, ye, x, ym + oy, x, ym];

    return {
        start: [x,ym],
        curves: [c1,c2,c3,c4]
    }
}

//使用path绘制矩形 x,y为起始点
export function createRectPath(start, end, path: SVGPathElement, styles?:any){
    // const p0 = [x, y];
    // const p1 = [x+w, y];
    // const p2 = [x+w, y+h];
    // const p3 = [x, y+h];
    const [ p0,p1,p2,p3 ] = getRectPointsByStartAndEnd(start,end);
    if(!p0)return [];
    const pathStr = [p0, p1, p2, p3].map((v,i)=>{ 
        return (i==0?'M':'L') + ` ${v[0]} ${v[1]}`
    }).join(' ');
    let rectStyle = styles || commonStyles;
    setElementAttribute(path,{
        ...rectStyle,
        d: `${pathStr} Z`
    })
    return [p0,p1,p2,p3]
}

//根据起点和终点获取矩形的四个点
export function getRectPointsByStartAndEnd(start,end){
    const p0 = { x: start[0], y: start[1] };
    const p1 = { x: end[0], y: end[1] };
    let x;
    let y;
    let w = Math.abs(p1.x - p0.x),h = Math.abs(p1.y - p0.y);

    //四种方向情况
    if (p1.x > p0.x && p1.y > p0.y) {
        x = p0.x;
        y = p0.y;
    } else if (p1.x > p0.x && p1.y < p0.y) {
        x = p0.x;
        y = p1.y;
    } else if (p1.x < p0.x && p1.y > p0.y) {
        x = p1.x;
        y = p0.y;
    } else if (p1.x < p0.x && p1.y < p0.y) {
        x = p1.x;
        y = p1.y;
    }

    return [ [x, y], [x+w, y], [x+w, y+h], [x, y+h] ]
}

export function segmentsIntr(a, b, c, d){  
  
    /** 1 解线性方程组, 求线段交点. **/  
    // 如果分母为0 则平行或共线, 不相交  --- 求出斜率再判断
    var denominator = (b.y - a.y)*(d.x - c.x) - (a.x - b.x)*(c.y - d.y);  
    if (denominator==0) {  
        return false;  
    }  
    
// 线段所在直线的交点坐标 (x , y)      
    var x = ( (b.x - a.x) * (d.x - c.x) * (c.y - a.y)   
                + (b.y - a.y) * (d.x - c.x) * a.x   
                - (d.y - c.y) * (b.x - a.x) * c.x ) / denominator ;  
    var y = -( (b.y - a.y) * (d.y - c.y) * (c.x - a.x)   
                + (b.x - a.x) * (d.y - c.y) * a.y   
                - (d.x - c.x) * (b.y - a.y) * c.y ) / denominator;  
    
/** 2 判断交点是否在两条线段上 **/  
    if (  
        // 交点在线段1上  
        (x - a.x) * (x - b.x) <= 0 && (y - a.y) * (y - b.y) <= 0  
        // 且交点也在线段2上  
            && (x - c.x) * (x - d.x) <= 0 && (y - c.y) * (y - d.y) <= 0  
        ){  
    
        // 返回交点p  
        return {  
                x :  x,  
                y :  y  
            }  
    }  
    //否则不相交  
    return false  
    
}  

type TransformResult = {
    distance: number,
    angle: number,
    result: [number,number]
}

interface DragMoveFunc {
    (data: TransformResult): void;
}

export class elementDrag {
    public $el: any;
    public isMouseDown: boolean;
    public mouseDownPoint: [number, number];
    public moveChange: DragMoveFunc;
    public moveEnd: DragMoveFunc;

    constructor(el:any,moveChange:DragMoveFunc, moveEnd: DragMoveFunc){
        this.$el = el;
        this.isMouseDown = false;
        this.mouseDownPoint = [0,0];
        this.moveChange = moveChange;
        this.moveEnd = moveEnd;
        this.bindEvent();
        
    }
    getTransformData(e){
        const startPoint = this.mouseDownPoint;
        const endPoint = [e.clientX,e.clientY];
        //计算角度和位移
        const line = [startPoint,endPoint];
        const distance = geometric.lineLength(line);
        const angle = geometric.lineAngle(line);
        const position = geometric.pointTranslate(startPoint,angle,distance);
        return { distance, angle, position }
    }
    bindEvent(){
        this.$el.addEventListener('mousedown', (e:MouseEvent) => {
            e.preventDefault();
            this.isMouseDown = true;
            this.mouseDownPoint = [e.clientX,e.clientY];
        });
        document.addEventListener('mousemove', (e) => {
            e.preventDefault();
            //拖拽事件
            if (this.isMouseDown) {
                // const startPoint = this.mouseDownPoint;
                // const endPoint = [e.clientX,e.clientY];
                // //计算角度和位移
                // const line = [startPoint,endPoint];
                // const distance = geometric.lineLength(line);
                // const angle = geometric.lineAngle(line);
                // const position = geometric.pointTranslate(startPoint,angle,distance);
                this.moveChange(this.getTransformData(e));
            }
        });
        document.addEventListener('mouseup', (e) => {
            e.preventDefault();
            //if(this.moveEnd)this.moveEnd(this.getTransformData(e));
            this.isMouseDown = false;
        })

    }
}

export function threeBezier(t, p1, cp1, cp2, p2) {
    const [x1, y1] = p1;
    const [x2, y2] = p2;
    const [cx1, cy1] = cp1;
    const [cx2, cy2] = cp2;
    let x =
        x1 * (1 - t) * (1 - t) * (1 - t) +
        3 * cx1 * t * (1 - t) * (1 - t) +
        3 * cx2 * t * t * (1 - t) +
        x2 * t * t * t;
    let y =
        y1 * (1 - t) * (1 - t) * (1 - t) +
        3 * cy1 * t * (1 - t) * (1 - t) +
        3 * cy2 * t * t * (1 - t) +
        y2 * t * t * t;
    return [x, y];
}

export function angleToRadian(angle){
    return Math.PI/180*angle
}

export function radianToAngle(radin){
    return 180/Math.PI*radin
}